﻿//---------------------------------------------------------------------------------
// Copyright 2011 Microsoft Corporation
// Licensed under the Microsoft Public License (MS-PL) (the "License"); 
// You may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.opensource.org/licenses/ms-pl.html
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR 
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
// INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR 
// CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
// MERCHANTABLITY OR NON-INFRINGEMENT. 
// See the Microsoft Public License (MS-PL) for the specific language governing 
// permissions and limitations under the License.
//---------------------------------------------------------------------------------

using System;
using System.IO;
using System.Speech.Recognition;
using System.Speech.Synthesis;
using System.Threading;
using Microsoft.Telco.Web.Contracts;

namespace Microsoft.Telco.Web.Services.Speech
{
    /// <summary>
    /// Class responsible for interacting with speech APIs to perform speech generation and recognition functions.
    /// </summary>
    internal class SpeechManager : IDisposable
    {

        ReaderWriterLockSlim synthLock = new ReaderWriterLockSlim();
        SpeechSynthesizer synthesizer;

        ReaderWriterLockSlim recognitionLock = new ReaderWriterLockSlim();
        SpeechRecognitionEngine recognitionEngine;

        #region Ctors and Cleanup

        /// <summary>
        /// Initializes a new instance of the <see cref="SpeechManager" /> class.
        /// </summary>
        public SpeechManager()
        {
            // Init the synthesizer
            synthesizer = new SpeechSynthesizer();
            // TODO: Set parameters for synthesizing based on culture, parms, or whatever

            recognitionEngine = new SpeechRecognitionEngine();
            // TODO: Set parameters for recognition based on culture, parms, or whatever

        }

        #endregion

        /// <summary>
        /// Transcribes the voice.
        /// </summary>
        /// <remarks>
        /// This method currently only accepts a byte array that contains WAV contents.  The filename is ignored in determining what sort of contents are contained within.
        /// </remarks>
        /// <param name="attachment">The attachment.</param>
        /// <returns></returns>
        public string TranscribeVoice(Attachment attachment)
        {
            if (attachment == null)
                throw new ArgumentNullException("attachment");

            recognitionLock.EnterWriteLock();

            try
            {

                // TODO: Confirm filetype and act appropriately instead of assuming WAV stream.

                using (MemoryStream stream = new MemoryStream(attachment.Content))
                {

                    try
                    {

                        recognitionEngine.SetInputToWaveStream(stream);
                        recognitionEngine.LoadGrammar(new DictationGrammar());
                        RecognitionResult result = recognitionEngine.Recognize();

                        if (result != null)
                            return result.Text;

                        else return string.Empty;

                    }
                    catch (Exception ex)
                    {
                        return ex.Message;
                    }

                }


            }
            finally
            {
                recognitionLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Converts the text to speech.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
        public Attachment ConvertTextToSpeech(string text)
        {
            if (string.IsNullOrEmpty(text))
                throw new ArgumentNullException("text");

            Attachment result = new Attachment() { FileName = "tts.wav" };

            // Lock so that we don't try to use the synthesizer twice at the same time
            synthLock.EnterWriteLock();

            try
            {

                using (MemoryStream stream = new MemoryStream())
                {

                    // Set output to the current stream and emit the text
                    synthesizer.SetOutputToWaveStream(stream);
                    synthesizer.Speak(text);

                    // Set the content and return the result
                    result.Content = stream.ToArray();
                    return result;

                }

            }
            finally
            {
                synthLock.ExitWriteLock();
            }
        }

        #region Finalizers and Cleanup

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="SpeechManager"/> is reclaimed by garbage collection.
        /// </summary>
        ~SpeechManager()
        {
            Dispose(false);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        internal void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (synthesizer != null)
                    synthesizer.Dispose();
                if (recognitionEngine != null)
                    recognitionEngine.Dispose();
            }
        }
        #endregion


    }
}